package org.six11.ss.client;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

import org.six11.ss.shared.GameStatusConstants;
import org.six11.ss.shared.Message;
import org.six11.util.Debug;
import org.six11.util.adt.MultiState;
import org.six11.util.adt.SynchronizedQueue;
import org.six11.util.gui.ColoredTextPane;

/**
 * This is the chat component, but it can also be used to display messages from the server, and
 * accept "slash-commands" from the user. Messages may appear in color!
 * 
 * @author Gabe Johnson <johnsogg@cmu.edu>
 */
public class TextConsole extends JComponent {

  private static final Color COLOR_STATUS = Color.GREEN;
  private static final Color COLOR_CHAT_FROM = Color.BLUE;
  private static final Color COLOR_CHAT_CONTENT = Color.BLACK;
  private static final Color COLOR_WARNING = Color.RED;
  private static final Color COLOR_LABEL_PREFIX = Color.MAGENTA;
  private static final Color COLOR_LABEL_CONTENT = Color.BLACK;
  private static final Color COLOR_LABEL_CLUE = new Color(205, 102, 0);

  private MultiState state;
  private static final String HELP_TEXT = "<type stuff here>";

  private ColoredTextPane serverMessages;
  private JTextField clientCommands;
  private JScrollPane serverMessagesScroller;

  public TextConsole(MultiState state) {
    super();
    this.state = state;
    setBackground(Color.WHITE);
    serverMessages = new ColoredTextPane();
    serverMessages.addFocusListener(new FocusAdapter() {
      public void focusGained(FocusEvent e) {
        focusTextInput();
      }
    });
    serverMessages.setEditable(false);
    serverMessages.setAutoscroll(true);
    serverMessagesScroller = new JScrollPane(serverMessages);
    clientCommands = new JTextField();
    ActionListener sendAction = new ActionListener() {
      public void actionPerformed(ActionEvent ev) {
        handleInput(ev.getActionCommand());
        clientCommands.setText("");
      }
    };
    clientCommands.addFocusListener(new FocusAdapter() {
      public void focusGained(FocusEvent e) {
        if (clientCommands.getText().equals(HELP_TEXT)) {
          clientCommands.setText("");
        }
      }
    });
    clientCommands.setText(HELP_TEXT);
    clientCommands.addActionListener(sendAction);
    clientCommands.requestFocusInWindow();
    clientCommands.setFont(new Font("Dialog", Font.PLAIN, 18));
    setLayout(new BorderLayout());
    add(serverMessagesScroller, BorderLayout.CENTER);
    add(clientCommands, BorderLayout.SOUTH);
    state.setValue(ClientStateNames.K_TEXT_CONSOLE, this);
  }

  public void focusTextInput() {
    clientCommands.requestFocus();
  }

  private void handleInput(String input) {
    if (input.length() > 0) {
      if (input.startsWith("/") && input.length() > 1) {
        handleCommand(input.substring(1));
      } else {
        sendChat(input);
      }
    }
  }

  @SuppressWarnings("unchecked")
  private void handleCommand(String cmd) {
    // this is the command without the leading slash. So if you type '/join foo', this function
    // receives 'join foo'.
    try {
      String trimmed = cmd.trim();
      int firstSpace = trimmed.indexOf(' ');
      String msgType = "unknown";
      String args = "";
      if (firstSpace < 0) {
        msgType = cmd;
      } else {
        msgType = cmd.substring(0, firstSpace);
        args = cmd.substring(firstSpace).trim();
      }
      bug("type: " + msgType);
      Map<String, CommandHandler> commandHandlers = (Map<String, CommandHandler>) state
          .getValue(ClientStateNames.K_COMMAND_HANDLERS);
      if (commandHandlers.containsKey(msgType)) {
        commandHandlers.get(msgType).handleCommand(args);
      } else {
        bug("Unknown command: " + msgType);
      }

    } catch (Exception ex) {
      bug("Failed to handle command properly. Original input string on next line:\n" + cmd);
      ex.printStackTrace();
    }
  }

  @SuppressWarnings("unchecked")
  private void sendChat(String input) {
    // give users a warning when they send during the notify roles phase, and don't send.
    String st = state.getString(ClientStateNames.K_GAME_STATUS);
    String role = state.getString(ClientStateNames.K_ROLE);
    if (st.equals(GameStatusConstants.STATUS_NOTIFY_ROLES)) {
      addWarning("Your message wasn't sent. We're about to start!");
    } else if (st.equals(GameStatusConstants.STATUS_PLAY) && role.equals("sketcher")) {
      addWarning("Sneaky. But no.");
    } else {
      bug("chat: " + input);
      SynchronizedQueue<Message> q = (SynchronizedQueue<Message>) state
          .getValue(ClientStateNames.K_TRANSMIT_QUEUE);
      Message msg = new Message("chat");
      msg.addParam("content", input);
      q.add(msg);
    }
  }

  private void bug(String what) {
    Debug.out("PicturephoneConsole", what);
  }

  /**
   * Adds a colored status string to the server messages console.
   * 
   * @param statusString
   *          the string to appear.
   */
  public void addStatus(String statusString) {
    serverMessages.append(COLOR_STATUS, statusString + "\n");
  }

  public void addWarning(String warningString) {
    serverMessages.append(COLOR_WARNING, warningString + "\n");
  }

  public void addPlayerList(List<String> players) {
    if (players.size() > 0) {
      serverMessages.append(COLOR_LABEL_PREFIX, "People in this room: ");
    }
    for (int i = 0; i < players.size(); i++) {
      serverMessages.append(COLOR_CHAT_FROM, players.get(i)
          + ((i == players.size() - 1) ? "" : ", "));
    }
    if (players.size() > 0) {
      serverMessages.append(COLOR_CHAT_FROM, "\n");
    }
  }

  /**
   * Adds a colored chat to the console output.
   * 
   * @param from
   *          the nickname of the sending player
   * @param content
   *          the message string
   */
  public void addChat(String from, String content) {
    serverMessages.append(COLOR_CHAT_FROM, from + ": ");
    serverMessages.append(COLOR_CHAT_CONTENT, content + "\n");
  }

  /**
   * @param param
   */
  public void addLabel(String userLabel) {
    serverMessages.append(COLOR_LABEL_PREFIX, "Your label: ");
    serverMessages.append(COLOR_LABEL_CONTENT, userLabel + "\n");
  }

  public void addOtherPlayerLabel(String nick, String userLabel) {
    serverMessages.append(COLOR_LABEL_PREFIX, nick + ": ");
    serverMessages.append(COLOR_LABEL_CONTENT, userLabel + "\n");
  }

  public void addClue(String clue) {
    serverMessages.append(COLOR_LABEL_PREFIX, "The clue was: ");
    serverMessages.append(COLOR_LABEL_CLUE, clue + "\n");
  }

}
